﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Rung.Net.Events;
using Rung.Net.Enums;
using System.IO;

namespace Rung.Net.Cache
{
    public class CacheClientStream : CacheStream, IDisposable
    {
        FileStream fs;
        SendingParamters para;
        public event CacheProcessChangedEventHandler OnCacheProcessChanged;
        public event FileTransferComplitedEventHandler OnFileTransferComplited;

        public CacheClientStream(System.Net.Sockets.NetworkStream ns, System.Net.EndPoint ep)
            : base(ns, ep) { }

        protected override void ReceivedRaw(byte[] bytes)
        {
            base.ReceivedRaw(bytes);

            string message = NetUtility.C(bytes);
            int type = BitConverter.ToInt32(bytes, 0);
            // 每个包实际容纳的文件数据
            byte[] bContent = new byte[bytes.Length - IntLength];
            // 数据包类型
            byte[] bType = BitConverter.GetBytes(MessageTypeBase.Cache_CachePackage);

            switch (type)
            {
                case MessageTypeBase.Cache_PackageMaxLength2:// 【接收方→发送方】：握手请求（二次握手），正式发送数据
                    {
                        #region 解析“二次握手”包
                        // 接收方的包容量
                        int revMaxLength = BitConverter.ToInt32(bytes, IntLength);

                        // 发送方和接收方的包容量选择一个较小的
                        if (revMaxLength < base.NetBasePackageMaxLength) base.NetBasePackageMaxLength = revMaxLength;
                        #endregion

                        base.packageCountReceived = 0;
                        base.fileLength = tempFile.Length;

                        #region 开始发送
                        this.para = new SendingParamters(-1, base.NetBasePackageMaxLength, 0, NetBasePackageMaxLength);

                        #region 保证 fs（FileStream）可用
                        if (fs == null)
                        {
                            fs = tempFile.OpenRead();
                        }
                        if (fs.CanWrite == false)
                        {
                            fs.Close();
                            fs = tempFile.OpenRead();
                        }
                        #endregion

                        try
                        {
                            // 发送
                            this.ReadFileSendPackage(0, 0);

                            System.Threading.Thread.Sleep(10);
                        }
                        catch
                        {
                            this.DisposeFileStream();
                            throw;
                        }
                        #endregion
                    }
                    break;
                case MessageTypeBase.Cache_CachePackageReceived:// 【接收方→发送方】：收到包
                    {
                        // 每个【Cache_CachePackageReceived】包头包括【数据包类型】【总包数】【包索引】【已接收包数】四个 int 值
                        // 总包数
                        int packageCount = BitConverter.ToInt32(bytes, IntLength);
                        // 包索引
                        int index = BitConverter.ToInt32(bytes, 2 * IntLength);
                        // 已收包数
                        base.packageCountReceived = BitConverter.ToInt32(bytes, 3 * IntLength);

                        if (this.OnCacheProcessChanged != null)
                        {
                            this.OnCacheProcessChanged(this, new CacheProcessChangedEventArgs(index, packageCount, base.packageCountReceived));
                        }
                        #region 继续发送
                        if (base.packageCountReceived < packageCount)// 还没收到最后一包
                        {
                            // 发送
                            this.ReadFileSendPackage(index + 1, base.packageCountReceived);
                        }
                        else if (base.packageCountReceived == packageCount)// 已收到最后一包
                        {
                            this.DisposeFileStream();

                            // 标记为清闲
                            this.IsBusy = false;

                            if (this.OnFileTransferComplited != null)
                            {
                                base.DoRaiseOnReceived();
                                this.OnFileTransferComplited(this, new FileTransferComplitedEventArgs(base.Guid, tempFile.FullName, tempFile.Length));
                            }
                        }
                        else// 断言不可能出现
                        {
                            //throw (new Exception("接收方收到的包数不可能大于总包数！"));
                        }
                        #endregion
                    }
                    break;
                case MessageTypeBase.Cache_Resend:// 【接收方→发送方】：重新发送当前包
                    {
                        // 包索引
                        int index = BitConverter.ToInt32(bytes, IntLength);

                        this.ReadFileSendPackage(index, base.packageCountReceived);
                        NetUtility.Log("LogC.txt", string.Format("重发\t{0}\r\n", index));
                    }
                    break;
            }
        }

        /// <summary>
        /// 读取文件的指定部位数据，并将其发送出去。
        /// 包头包括【数据包类型】、【总包数】和【包索引】三个 int 值。
        /// </summary>
        /// <param name="index">本次发送包的索引</param>
        /// <param name="received">已接收包数</param>
        private void ReadFileSendPackage(int index, int received)
        {
            // 包类型
            byte[] bType = BitConverter.GetBytes(MessageTypeBase.Cache_CachePackage);
            // 每个完整的【Cache_CachePackage】包实际容纳的文件数据长度（抛去包头，包头包括【数据包类型】【总包数】【包索引】【CRC校验】四个 int 值）
            int contentLength = this.NetBasePackageMaxLength - IntLength * 4;
            // 正文内容
            this.buffer = new byte[base.NetBasePackageMaxLength];
            byte[] bContent = new byte[contentLength];
            // 包索引
            byte[] bI = BitConverter.GetBytes(index);
            // 总包数
            int packageCount = (int)Math.Ceiling((decimal)base.fileLength / (decimal)contentLength);
            byte[] bPC = BitConverter.GetBytes(packageCount);

            if (received + 1 < packageCount)
            {
                //if (fs.Position == fs.Length) return;
                #region 文件流跳到 index 位置
                long position = index * (base.NetBasePackageMaxLength - 4 * IntLength);
                if (position == fs.Length)
                {
                    NetUtility.Log("LogS.txt", string.Format("文件到尾\t{0}\r\n", index));
                    return;
                }
                if (index >= packageCount || index < 0)
                {
                    NetUtility.Log("LogS.txt", string.Format("索引异常\t{0}\r\n", index));
                    return;
                }
                if (fs.Position != position)
                {
                    NetUtility.Log("LogS.txt", string.Format("指针异常\t{0}\r\n", index));
                    fs.Position = position;
                }
                #endregion
                fs.Read(bContent, 0, contentLength);

                // CRC校验值（若为最后一包，bContent 尾部的剩余空间用 0 补齐）
                byte[] crc = CRC32.CrcToBytes(bContent);
#if DEBUG
                if (this.IsBytesAre0(bContent))
                {
                    NetUtility.Log("LogC.txt", string.Format("零包0\t{0}\r\n", index));
                }

                uint iCrc = BitConverter.ToUInt32(crc, 0);
                if (iCrc == 68705593)
                    NetUtility.Log("LogC.txt", string.Format("空包\t{0}\r\n", index)); 
#endif

                NetUtility.BlockCopy(bType, this.buffer, 0);
                NetUtility.BlockCopy(bPC, this.buffer, IntLength);
                NetUtility.BlockCopy(bI, this.buffer, 2 * IntLength);
                NetUtility.BlockCopy(crc, this.buffer, 3 * IntLength);
                NetUtility.BlockCopy(bContent, this.buffer, 4 * IntLength);

                base.SendRaw(buffer);
            }
            else if (received + 1 == packageCount)// 发送最后一包
            {
                int length = (int)(base.fileLength - contentLength * (packageCount - 1));
                fs.Read(bContent, 0, length);
#if DEBUG
                if (this.IsBytesAre0(bContent))
                {
                    NetUtility.Log("LogC.txt", string.Format("零包1\t{0}\r\n", index));
                }
                if (BitConverter.ToInt32(bContent, 0) == 0 && BitConverter.ToInt32(bContent, 10) == 0 && BitConverter.ToInt32(bContent, 30) == 0)
                {
                    NetUtility.Log("LogC.txt", string.Format("零包2\t{0}\r\n", index));
                }
#endif

                // CRC校验值
                byte[] crc = CRC32.CrcToBytes(bContent);

                NetUtility.BlockCopy(bType, this.buffer, 0);
                NetUtility.BlockCopy(bPC, this.buffer, IntLength);
                NetUtility.BlockCopy(bI, this.buffer, 2 * IntLength);
                NetUtility.BlockCopy(crc, this.buffer, 3 * IntLength);
                NetUtility.BlockCopy(bContent, this.buffer, 4 * IntLength);

                base.SendRaw(buffer);
            }
        }

        private bool IsBytesAre0(byte[] bContent)
        {
                for (int i = 0; i < bContent.Length; i++)
                {
                    if (bContent[i] != 0) return false;
                }
                return true;
        }

        /// <summary>
        /// 干掉该对象的文本流变量（this.fs）。
        /// </summary>
        private void DisposeFileStream()
        {
            fs.Close();
            fs.Dispose();
        }

        #region 发送过程中所需数据的集合类
        internal class SendingParamters
        {
            /// <summary>
            /// 待发送数据的缓存。
            /// </summary>
            byte[] Buffer;

            /// <summary>
            /// 接收方通知的已接受的包索引。
            /// </summary>
            int LatestIndex;

            /// <summary>
            /// 当前待发送包的索引。
            /// </summary>
            int CurrentIndex { get { return LatestIndex + 1; } }

            /// <summary>
            /// 总包数。
            /// </summary>
            int PackageCountTotal;

            /// <summary>
            /// 已发送包的总数。
            /// </summary>
            int PackageCountReceived;

            /// <summary>
            /// 数据包类型。
            /// </summary>
            byte[] bType;

            /// <summary>
            /// 当前待发送包（【总包数】+【包索引】+【包体】，不包括包类型）。
            /// </summary>
            byte[] bBody;

            byte[] bPackageCountTotal
            { get { return BitConverter.GetBytes(PackageCountTotal); } }

            /// <summary>
            /// 当前待发送包的长度。
            /// </summary>
            int RealLength;

            internal SendingParamters(int latestIndex, int totalCount, int receivedCount, int packageMaxLength)
            {
                this.bType = BitConverter.GetBytes(MessageTypeBase.Cache_CachePackage);
                this.Buffer = new byte[packageMaxLength];
                this.LatestIndex = latestIndex;
                this.PackageCountTotal = totalCount;
                this.PackageCountReceived = receivedCount;
                this.RealLength = packageMaxLength;
            }
        }
        #endregion

        #region IDisposable 成员

        public void Dispose()
        {
            base.Dispose();

            if (fs != null)
            {
                this.DisposeFileStream();
            }
        }

        #endregion
    }
}
